package slog

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"flag"
	"gitee.com/yysf_xin/dplog/pkg/extracter"
	"gitee.com/yysf_xin/go-common/tools"
	"io"
	"os"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var (
	LEVEL   = []byte{'D', 'I', 'W', 'E'}
	BufPool = sync.Pool{
		New: func() interface{} {
			return new(bytes.Buffer)
		}}
)

type Reset interface {
	Reset()
}

type Message interface {
	Reset
	GetTime() *time.Time
	GetLogger() string
	GetMethod() string
	GetThread() string
	GetLevel() string
	GetTrace() string
	GetMessage() []byte
	GetSubMessage() []byte
	SetSubMessage(sub []byte)
}

type MessageRef struct {
	Buf  *bytes.Buffer
	Meta MetaData
	// 仅用于次数统计
	Count    int
	len      int
	refCount int32
	refPool  *sync.Pool
	ref      Message
}

func NewRef(ref Message) *MessageRef {
	return &MessageRef{
		ref: ref,
	}
}

func (m *MessageRef) GetRefCount() int32 {
	return atomic.LoadInt32(&m.refCount)
}

func (m *MessageRef) Retain() {
	atomic.AddInt32(&m.refCount, 1)
}

func (m *MessageRef) Release() {
	v := atomic.AddInt32(&m.refCount, -1)
	if v == 0 && m.Buf != nil {
		BufPool.Put(m.Buf)
		if m.refPool != nil {
			m.refPool.Put(m)
		}
		m.Buf = nil
	}
}

func (m *MessageRef) Reset() {
	m.Buf.Reset()
	if m.ref != nil {
		m.ref.Reset()
	}
	m.refCount = 1
}

func (m *MessageRef) GetRef() Message {
	return m.ref
}

func (m *MessageRef) RestSubMessage() {
	l := m.Buf.Len()
	if l != m.len {
		m.ref.SetSubMessage(m.Buf.Bytes()[m.len:l])
	}
}

type Extractor interface {
	Extract(line []byte) (bool, error)
}
type MetaData struct {
	Line       int
	Offset     int64
	File       string
	Env        string
	Ip         string
	OriginSize int
}
type Options struct {
	MultiLine bool
}
type MakerOption interface {
	Options() *Options
}

type MsgMaker interface {
	Extractor
	New() Message
	Filter(ref *MessageRef) bool
	Custom(c *Collect)
}

type LineMsgMaker struct {
	Name string
}

func (l *LineMsgMaker) Custom(c *Collect) {
	c.Opt.MultiLine = false
}

func (l *LineMsgMaker) Extract(line []byte) (bool, error) {
	return true, nil
}

func (l *LineMsgMaker) New() Message {
	return new(MessageImpl)
}

func (l *LineMsgMaker) Filter(ref *MessageRef) bool {
	msg := ref.GetRef().(*MessageImpl)
	msg.Time = time.Now()
	msg.Level = "DEBUG"
	msg.AppName = l.Name
	msg.Message = tools.BytesToString(ref.Buf.Bytes())
	return false
}

type Emitter interface {
	FireMsg(msg *MessageRef)
}

type Emitters struct {
	items []Emitter
}

func (a *Emitters) Append(e Emitter) {
	a.items = append(a.items, e)
}
func (a *Emitters) FireMsg(msg *MessageRef) {
	for _, v := range a.items {
		v.FireMsg(msg)
	}
}

func NewEmitters(em ...Emitter) (v *Emitters) {
	v = &Emitters{
		items: em,
	}
	return
}

type LineListener interface {
	Init(m *MetaData)
	OnRead(line []byte, isPrefix bool)
	Flush()
}

type FileReader struct {
	*os.File
	l LineListener
	r *bufio.Reader
}

func (r *FileReader) Run() {
	var (
		line     []byte
		isPrefix bool
		err      error
		i        int
	)
	for {
		line, isPrefix, err = r.r.ReadLine()
		if err != nil {
			break
		}
		i++
		r.l.OnRead(line, isPrefix)
	}
	r.l.Flush()
}

type Collect struct {
	MsgMaker
	Emitter
	Opt        *Option
	sub        extracter.SubLine
	pool       *sync.Pool
	isPrefix   bool
	prev, curr *MessageRef
	m          *MetaData
}

func (c *Collect) Init(m *MetaData) {
	c.m = m
}

type Option struct {
	MaxBuf    int
	MultiLine bool
}

const (
	MinBuf = 1800
)

func NewCollect(op *Option, ex MsgMaker, em Emitter) (c *Collect) {
	if op.MaxBuf < MinBuf {
		op.MaxBuf = MinBuf
	}
	pool := new(sync.Pool)
	pool.New = func() interface{} {
		ref := new(MessageRef)
		ref.ref = ex.New()
		ref.refPool = pool
		return ref
	}
	c = &Collect{MsgMaker: ex, Emitter: em, pool: pool, Opt: op}
	if sub, ok := ex.(extracter.SubLine); ok {
		c.sub = sub
	}

	ex.Custom(c)

	return
}

func (c *Collect) Flush() {
	if c.prev != nil {
		c.prev.RestSubMessage()
		c.FireMsg(c.prev)
		c.prev.Release()
		c.prev = nil
		c.Rest()
	}
}
func (c *Collect) OnRead(line []byte, isPrefix bool) {
	// 检查上次状态 缓存溢出直接跳过
	if c.isPrefix {
		c.isPrefix = isPrefix
		if c.prev != nil {
			c.prev.Meta.OriginSize += len(line)
		}
		return
	}
	c.isPrefix = isPrefix

	if c.curr == nil {
		c.curr = c.pool.Get().(*MessageRef)
		c.curr.Buf = BufPool.Get().(*bytes.Buffer)
	}
	c.curr.Reset()
	c.curr.Buf.Write(line)

	if ok, _ := c.Extract(c.curr.Buf.Bytes()); ok {
		c.Flush()
		if c.Filter(c.curr) {
			c.curr.Release()
			c.curr = nil
			return
		}
		c.curr.len = c.curr.Buf.Len()
		c.prev = c.curr
		if c.m != nil {
			c.curr.Meta = *c.m
		}
		c.curr.Meta.OriginSize += len(line)
		c.curr = nil
		if !c.Opt.MultiLine {
			c.Flush()
		}
	} else if c.prev != nil {
		// 超过buf 丢弃
		if c.sub != nil && c.prev.Buf.Len() < c.Opt.MaxBuf {
			c.sub.SubExtract(line, c.prev.Buf)
		}
		c.prev.Meta.OriginSize += len(line)
	}

}

func (c *Collect) Rest() {
	if c.sub != nil {
		c.sub.Rest()
	}
}

func (c *Collect) Close() error {
	return nil
}

func NewFileReader(file string, l LineListener) (*FileReader, error) {
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	var r io.Reader = f
	if strings.HasSuffix(file, ".gz") {
		r, err = gzip.NewReader(r)
		if err != nil {
			_ = f.Close()
			return nil, err
		}
	}
	return &FileReader{File: f, l: l, r: bufio.NewReader(r)}, nil
}

func InitFlag(opt *Option) {
	flag.IntVar(&opt.MaxBuf, "max_buf", 2000, "max message size")
	flag.BoolVar(&opt.MultiLine, "multiLine", true, "multiLine")
}
